import math
import time

from particle import Particle
from force_generator import ForceGenerator
from vector import Vector

class Universe:
    '''Manages all particles and ForceGenerators'''

    def __init__(self):
        self.generators = []
        self.particles = []
        self.time_last_update = -1

    def show_all_particles(self):
        '''Display a key and tell each particle to show itself'''
        print("\n{0:15}{1:15}{2:15}{3:15}{4:15}{5:15}{6:15}"\
                .format('x', 'y', 'vx', 'vy', 'Fx', 'Fy', 'mass'))

        for current in self.particles:
            current.show()

    def show_all_force_generators(self):
        '''Tell each force generator to show itself.'''
        print('\n')
        for cur in self.generators:
            cur.show()

    def add_force_generator(self, force_gen):
        '''Make the universe aware of the given force generator.'''
        self.generators.append(force_gen)

    def add_particle(self, particle):
        '''Make the universe aware of the passed-in particle.'''
        self.particles.append(particle)

    def update(self):
        '''Make each force generator aware of each particle.
        Then move each particle according to the forces.'''
        # tell each generator that we are updating particles
        for current_generator in self.generators:
            current_generator.starting_run()
        # apply forces to each particle
        for current_particle in self.particles:
            current_particle.sum_forces = Vector(0, 0)
            for current_generator in self.generators:
                current_generator.act_on_particle(current_particle)

        # tell each particle to act on those forces
        time_diff = 0
        cur_time = time.time()
        if self.time_last_update != -1:
            time_diff = cur_time - self.time_last_update
        self.time_last_update = cur_time
        for current_particle in self.particles:
            current_particle.update(time_diff)

    def get_closest(self, position):
        '''Returns the particle closest to the given position Vector.'''
        closest_particle = None
        closest_distance = 1e10000 #inf
        for test_particle in self.particles:
            disp = position - test_particle.position
            distance = math.hypot(disp.x, disp.y)
            if distance < closest_distance:
                closest_particle = test_particle
                closest_distance = distance
        return closest_particle
    
    def get_bounds_of_velocity(self):
        min = 1e10000 # inf
        max = -1e10000 # -inf
        for particle in self.particles:
            v = particle.velocity.magnitude()
            if v > max:
                max = v
            if v < min:
                min = v
        return (min, max)
